#include "instructions/loads/xaload.h"
#include "rtda/heap/object.h"
#include "instructions/base/utils.hpp"

namespace jvm {
    namespace instructions {

        void AALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto&& arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto&& refs = arrRef->refs();
            utils::checkIndex(refs.size(), index);
            stack->pushRef(refs[index]);
        }

        void BALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& bytes = arrRef->bytes();
            utils::checkIndex(bytes.size(), index);
            stack->pushInt(static_cast<int32_t>(bytes[index]));
        }

        void CALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& chars = arrRef->chars();
            utils::checkIndex(chars.size(), index);
            stack->pushInt(static_cast<int32_t>(chars[index]));
        }

        void DALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& doubles = arrRef->doubles();
            utils::checkIndex(doubles.size(), index);
            stack->pushDouble(doubles[index]);
        }

        void FALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& floats = arrRef->floats();
            utils::checkIndex(floats.size(), index);
            stack->pushFloat(floats[index]);
        }

        void IALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& ints = arrRef->ints();
            utils::checkIndex(ints.size(), index);
            stack->pushInt(ints[index]);
        }

        void LALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& longs = arrRef->longs();
            utils::checkIndex(longs.size(), index);
            stack->pushLong(longs[index]);
        }

        void SALOAD::execute(std::shared_ptr<rtda::Frame> pframe)
        {
            auto&& stack = pframe->getOperandStack();
            int32_t index = stack->popInt();
            auto arrRef = stack->popRef();

            utils::checkNotNil(arrRef);
            auto& shorts = arrRef->shorts();
            utils::checkIndex(shorts.size(), index);
            stack->pushInt(static_cast<int32_t>(shorts[index]));
        }

    } // namespace instructions
}// namespace jvm